/* 
 * Copyright (C) 2001-2003 Jacek Sieka, j_s@telia.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "stdinc.h"
#include "DCPlusPlus.h"

#include "DirectoryListing.h"

#include "QueueManager.h"
#include "SearchManager.h"

#include "StringTokenizer.h"
#include "ADLSearch.h"
#include "SimpleXML.h"
#include "FilteredFile.h"
#include "BZUtils.h"
#include "CryptoManager.h"
#include <stdexcept>
// CDM EXTENSION BEGINS
//
bool DirectoryListing::loadFile(const string& name, bool doAdl, bool checkSmallFile, bool checkFileList) {
	bool disconnect = false;
// CDM EXTENSION ENDS
	string txt;

	// For now, we detect type by ending...
	string ext = Util::getFileExt(name);
	if(Util::stricmp(ext, ".DcLst") == 0) {
		size_t len = (size_t)::File::getSize(name);
		AutoArray<u_int8_t> buf(len);
		::File(name, ::File::READ, ::File::OPEN).read(buf, len);
		CryptoManager::getInstance()->decodeHuffman(buf, txt);
		load(txt, doAdl);
	} else if(Util::stricmp(ext, ".bz2") == 0) {
		::File ff(name, ::File::READ, ::File::OPEN);
		FilteredInputStream<UnBZFilter, false> f(&ff);
		const size_t BUF_SIZE = 64*1024;
		char buf[BUF_SIZE];
		u_int32_t len;
		// pothead starts svn106 changed svn141 (thanks Trem)
		char last = 0;
		char last2 = 0;
		int count = 0;
		// pothead ends
		for(;;) {
			size_t n = BUF_SIZE;
			len = f.read(buf, n);
			txt.append(buf, len);
			if (len < BUF_SIZE)
				break;
			// pothead starts svn106 changed svn141 (thanks Trem)
			last = buf[len-1];
			if (last == last2) {
				count++;
			} else {
				count = 0;
			}
			if (count > 250) {
				break;
			}
			last2 = buf[len-1];
			// pothead ends

		}
		int64_t tick = GET_TICK();
		if(txt.compare(0, 5, "<?xml") == 0) {
			// CDM EXTENSION BEGINS
			//loadXML(txt, doAdl);
			disconnect = loadXML(txt, doAdl, checkSmallFile);
			// CDM EXTENSION ENDS
		} else {
			// CDM EXTENSION BEGINS
			//load(txt, doAdl);
			disconnect = load(txt, doAdl, checkSmallFile);
			// CDM EXTENSION ENDS
		}
		string msg = "Time taken: " + Util::toString(GET_TICK()-tick) + " milliseconds\n";
		dcdebug(msg.c_str());
	}
	// CDM EXTENSION BEGINS
	else {
		return false;
	}
	if(checkFileList) {
		int64_t realSize = getTotalSize(true);
		int64_t statedSize = user->getBytesShared();
		user->setFileListShared(realSize, getTotalFileCount(true));
		user->setHashedFilesShared(hashedFiles);
		
		//Create a version from the filelist generator.
		string genVer = user->getFilelistGenerator();
		string::size_type i = genVer.find(' ');
		if(i != string::npos ) {
			genVer = genVer.substr(i+1);
		}

		int hashed = 0;
		string sTagVer(user->getTagVersion());
		float fTagVer = Util::isFloat(sTagVer) ? Util::toFloat(sTagVer) : 0;
		float fGenVer = Util::isFloat(genVer) ? Util::toFloat(genVer) : 0;

		// pothead starts svn135 svn155
		if (fGenVer > 0 && fTagVer != fGenVer && user->getClaimsToBeDCPP() && ((user->getFilelistGenerator()).substr(0, 5) == "DC++ ")) {
			string cheat = (STRING(DC_STEALTH_SUCKS) + " " + STRING(FILELIST_MISMATCH));
			user->setCheat(cheat, false, true);
			user->displayCheat(user->getNick() + " " + cheat);
			user->logDetect(true);
			if(SETTING(TAG_FILELIST_GEN_MISMATCH)) 
				user->sendRawCommand(SETTING(TAG_FILELIST_GEN_MISMATCH), Client::CONTEXT_RAW_FILELIST);
		}
		// pothead ends
		// pothead starts svn189 (strong dc code)
		if (detectRMDC403D1()) {
			user->setDetectRMDC403D1(true);
		}
		if((user->getFilelistGenerator() == "DC++ 0.403")) {
			if(RegexpHandler::matchProfile(user->getTag(), "^<StrgDC\\+\\+ V:1.00 RC([89]){1}")) {
				string cheat = "rmDC++ in StrongDC++ " + user->getTagVersion() + " emulation mode";
				user->setClientType("rmDC++ rmDC++ 0.403");
				user->setCheat(cheat, true, false);
				if (BOOLSETTING(DISPLAY_RMDC_CHEAT))
					user->displayCheat(user->getNick() + " " + cheat);
				user->logDetect(true);
				if(SETTING(RMDC))
					user->sendRawCommand(SETTING(RMDC), Client::CONTEXT_RAW_GPL_BREAKING_SCUM);
			}
		}
		// pothead ends
		if(fGenVer > 0 && hashedFiles >= 0 && (fTagVer >= 0.307 || fGenVer >= 0.307) && user->getBytesShared() > 0) {
			//Hashing started with DC++ version 0.307, let us use both tag and listgenetaror as reference.
			hashed = static_cast<int>(100 * ((float)hashedFiles / ((float)getTotalFileCount(true))));
			user->setPercentHashed(Util::toString(hashed) + " %");

			if(BOOLSETTING(CHECK_HASH)){
				string cheat;
				if((fTagVer >= 0.4032 || fGenVer >= 0.4032) && (hashed < 98)) {
					cheat = STRING(ALL_FILES_SHOULD_BE_HASHED) + " " + genVer + STRING(HASHED) + " " + Util::toString(hashed) + '%';
					user->setCheat(cheat, false, true);
					//Sulan start 29
					user->displayCheat(user->getNick() + ' ' + cheat);
					//End
					user->logDetect(true);
					if(SETTING(HASH_ERROR_RAW)) {
						user->sendRawCommand(SETTING(HASH_ERROR_RAW), Client::CONTEXT_RAW_FILELIST);
					}
				}else if(hashed <= SETTING(PERCENT_HASHED)) {
				// Some fakefiles are not hashed, filelist generator wsDC sometimes have a few files hashed,
				//but i have never seen more than 20%
					cheat = STRING(LOW_AMOUNT_HASHED) + Util::toString(hashed) + '%';
					user->setCheat(cheat, false, true);
					//Sulan start 29
					user->displayCheat(user->getNick() + ' ' + cheat);
					//End
				}else if(hashedFiles > SETTING(PERCENT_HASHED)) {
					//We dont want to remove any cheats other that the ones that is set here
					user->sortCheatingString("All files should be hashed", false);
					user->sortCheatingString("Low amount of hashed files", false);
					user->setBadFilelist(false);
				}
			}
		}else{
			user->setPercentHashed(Util::emptyString);
		}

		double multiplier = ((100+(double)SETTING(PERCENT_FAKE_SHARE_TOLERATED))/100);
		int64_t sizeTolerated = (int64_t)(realSize*multiplier);
		if(statedSize > sizeTolerated && SETTING(FAKE_RAW)) {
			string detectString = "Mismatched share size - ";
			if(realSize == 0) {
				detectString += "zero bytes real size";
			} else {
				double qwe = (double)((double)statedSize / (double)realSize);
				detectString += "filelist was inflated " + Util::toString(qwe) + " times";
			}
			detectString += ", stated size = " + Util::formatBytes(statedSize) + ", real size = " + Util::formatBytes(realSize);
			user->setCheat(detectString, false, true);
			//Sulan start 29
			user->displayCheat(user->getNick() + ' ' + detectString);
			//End
			user->logDetect(true);
			if (SETTING(FAKE_RAW)) {
				user->sendRawCommand(SETTING(INFLATED_FILELIST_RAW), Client::CONTEXT_RAW_FILELIST);
			}
			user->setFilelistComplete();
			return disconnect; // faking is worse than forbidden i guess...
		}
		DirectoryListing::File::List forbiddenList = getForbiddenFiles();
		DirectoryListing::Directory::List forbiddenDirList = getForbiddenDirs();
		if(forbiddenList.size() > 0 || forbiddenDirList.size() > 0) {
			int64_t fs = 0;
			string s, c, sz, tth, stringForKick;
			int rawCommand = 0, totalPoints = 0, point = 0;
			bool forFromFavs = false;
			bool forOverRide = false;

			if(forbiddenList.size() > 0) {
				for(DirectoryListing::File::Iter i = forbiddenList.begin() ; i != forbiddenList.end() ; i++) {
					fs += (*i)->getSize();
					totalPoints += (*i)->getPoints();
					if ((*i)->getPoints() >= point ) {
						point = (*i)->getPoints();
						s = (*i)->getFullFileName();
						c = (*i)->getAdlsComment();
						if ((*i)->getTTH() != NULL)
							tth = (*i)->getTTH()->toBase32();
						sz = Util::toString((*i)->getSize());
						if ((*i)->getOverRidePoints()) {
							forOverRide = true;
							if ((*i)->getFromFavs()) {
								rawCommand = (*i)->getAdlsRaw();
								forFromFavs = true;
							} else {
								stringForKick = (*i)->getKickString();
								forFromFavs = false;
							}
						}
					}
				}
			}
			if(forbiddenDirList.size() > 0) {
				for(DirectoryListing::Directory::Iter j = forbiddenDirList.begin() ; j != forbiddenDirList.end() ; j++) {
					fs += (*j)->getTotalSize();
					totalPoints += (*j)->getPoints();
					if ((*j)->getPoints() >= point ) {
						point = (*j)->getPoints();
						s = (*j)->getName();
						c = (*j)->getAdlsComment();
						sz = Util::toString((*j)->getTotalSize());
						if ((*j)->getOverRidePoints()) {
							forOverRide = true;
							if ((*j)->getFromFavs()) {
								rawCommand = (*j)->getAdlsRaw();
								forFromFavs = true;
							} else {
								stringForKick = (*j)->getKickString();
								forFromFavs = false;
							}
						}
					}
				}
			}

			user->setAdlFile(s);
			s = Util::toString(totalPoints) + STRING(POINTS_IN) + Util::toString((int)forbiddenList.size() + (int)forbiddenDirList.size()) + " " + CSTRING(FORBIDDEN_FILES_INCLUDING) + ": " + s;
			user->displayCheat(user->getNick() + " " + STRING(HAS) + " " + s + (c.empty() ? "" :string( " " + STRING(COMMENT) + ": " + c)));
			user->setAdlsComment(c);
			user->setAdlFileSize(sz);
			user->setAdlTTH(tth);
			user->setAdlForbiddenSize(fs);
			user->sortCheatingString(s, true);
			user->setBadFilelist(true);
			user->updated();
			user->setFlag(User::FAILED_ADLSEARCH);
			user->setTimeForFilelistRecheck(GET_TICK());
			user->setTotalPoints(totalPoints);
			if (forOverRide) {
				if (forFromFavs)
					user->sendRawCommand(rawCommand, Client::CONTEXT_RAW_ADLS);
				else
					user->sendRawCommand(stringForKick, Client::CONTEXT_RAW_ADLS);
			} else if (totalPoints > 0) {
				if (totalPoints < 500)
					user->sendRawCommand(SETTING(ADL_RAW_LOW_POINTS), Client::CONTEXT_RAW_ADLS);
				else if (totalPoints < 5000)
					user->sendRawCommand(SETTING(ADL_RAW_MEDIUM_POINTS), Client::CONTEXT_RAW_ADLS);
				else
					user->sendRawCommand(SETTING(ADL_RAW_HIGH_POINTS), Client::CONTEXT_RAW_ADLS);
			}
			//dcdebug("Util::formatParams(rawCommand, ucParams) = %s\n", Util::formatParams(rawCommand, ucParams));
		}
		//user->addLine("*** User " + user->getNick() + " Filelist checked");
		user->setFilelistComplete();
	}
	return disconnect;
	// CDM EXTENSION ENDS
}
// CDM EXTENSION BEGINS
bool DirectoryListing::load(const string& in, bool doAdl, bool checkSmallFile) {
// CDM EXTENSION ENDS
	StringTokenizer<string> t(in, '\n');

	StringList& tokens = t.getTokens();
	string::size_type indent = 0;
	// CDM EXTENSION BEGINS
	foundSmallFile = !BOOLSETTING(CHECK_SMALL_FILES);
	// CDM EXTENSION ENDS
	// Prepare ADLSearch manager
	ADLSearchManager* pADLSearch = ADLSearchManager::getInstance();
	ADLSearchManager::DestDirList destDirs;

	StringMap params;
	if(doAdl) {
		params["nick"] = getUser()->getNick();
		pADLSearch->setUser(getUser());

		pADLSearch->PrepareDestinationDirectories(destDirs, root, params);
		pADLSearch->setBreakOnFirst(BOOLSETTING(ADLS_BREAK_ON_FIRST));
	}

	Directory* cur = root;
	string fullPath;
	
	for(StringIter i = tokens.begin(); i != tokens.end(); ++i) 
	{
		string& tok = *i;
		string::size_type j = tok.find_first_not_of('\t');
		if(j == string::npos) {
			break;
		}

		while(j < indent) {
			// Wind up directory structure
			cur = cur->getParent();
			dcassert(cur != NULL);
			indent--;
			string::size_type l = fullPath.find_last_of('\\');
			if(l != string::npos) {
				fullPath.erase(fullPath.begin() + l, fullPath.end());
			}
			if(doAdl)
				pADLSearch->StepUpDirectory(destDirs);
		}

		string::size_type k = tok.find('|', j);
		if(k != string::npos) {
			// this must be a file...
			int64_t size = Util::toInt64(tok.substr(k+1));
			cur->files.push_back(new File(cur, tok.substr(j, k-j), size == -1 ? 0 : size));
			// ADLSearch
			// CDM EXTENSION BEGINS
			//if(doAdl)
				//pADLSearch->MatchesFile(destDirs, cur->files.back(), fullPath);
			if(doAdl) {
				if(checkSmallFile && !foundSmallFile) {
					foundSmallFile = pADLSearch->MatchesFile(destDirs, cur->files.back(), fullPath, true);
					if(foundSmallFile) {
						download(cur->files.back(), Util::getAppPath() + "SmallFiles\\" + cur->files.back()->getName(), false, true);
					}
				} else {
					pADLSearch->MatchesFile(destDirs, cur->files.back(), fullPath);
				} 
			}
			// CDM EXTENSION ENDS
		} else {
			// A directory
			string name = tok.substr(j, tok.length()-j-1);
			fullPath += '\\';
			fullPath += name;

			Directory::Iter di = ::find(cur->directories.begin(), cur->directories.end(), name);
			if(di != cur->directories.end()) {
				cur = *di;
			} else {
				Directory* d = new Directory(cur, name);
				cur->directories.push_back(d);
				cur = d;
			}
			if(doAdl)
				pADLSearch->MatchesDirectory(destDirs, cur, fullPath);
			indent++;
		}
	}

	// Finalize ADLSearch manager
	if(doAdl)
		pADLSearch->FinalizeDestinationDirectories(destDirs, root);
	// CDM EXTENSION BEGINS
	return !foundSmallFile;
	// CDM EXTENSION ENDS
}

class ListLoader : public SimpleXMLReader::CallBack {
public:
	// CDM EXTENSION BEGINS
	ListLoader(DirectoryListing::Directory* root, const User::Ptr& aUser, bool aDoAdl, bool aCheckSmallFile, DirectoryListing* aParent) : cur(root), inListing(false), doAdl(aDoAdl), checkSmallFile(aCheckSmallFile), foundSmallFile(false), dirListing(aParent), user(aUser) { 
	// CDM EXTENSION ENDS
		if(doAdl) {
			//Testing this as it is the only thing i can think of
			if(user){
				params["nick"] = user->getNick();
				ADLSearchManager::getInstance()->setUser(aUser);
			}

			ADLSearchManager::getInstance()->PrepareDestinationDirectories(destDirs, root, params);
			ADLSearchManager::getInstance()->setBreakOnFirst(BOOLSETTING(ADLS_BREAK_ON_FIRST));
		}

		lastFileIter = cur->files.begin();
	};

	virtual ~ListLoader() { }

	virtual void startTag(const string& name, StringPairList& attribs, bool simple);
	virtual void endTag(const string& name, const string& data);
private:
	string fullPath;

	ADLSearchManager::DestDirList destDirs;
	DirectoryListing::Directory* cur;
	DirectoryListing::File::Iter lastFileIter;
	DirectoryListing* dirListing;

	StringMap params;
	bool inListing;
	bool doAdl;
	// CDM EXTENSION BEGINS
	User::Ptr user;
	bool checkSmallFile;
	GETSET(bool, foundSmallFile, FoundSmallFile);
	// CDM EXTENSION ENDS
};
// CDM EXTENSION BEGINS
//void DirectoryListing::loadXML(const string& xml, bool doAdl) {
bool DirectoryListing::loadXML(const string& xml, bool doAdl, bool checkSmallFile /* = false */) {
// CDM EXTENSION ENDS
	setUtf8(true);

	ListLoader ll(getRoot(), getUser(), doAdl, checkSmallFile, this);
	SimpleXMLReader(&ll).fromXML(xml);
	// CDM EXTENSION BEGINS
	return true/*ll.getFoundSmallFile()*/;
	// CDM EXTENSION ENDS
}

static const string sFileListing = "FileListing";
static const string sDirectory = "Directory";
static const string sFile = "File";
static const string sName = "Name";
static const string sSize = "Size";
static const string sTTH = "TTH";
//	LOG("regexp","3]" + Util::toString(Util::getMemoryUsageAsInt()));
void ListLoader::startTag(const string& name, StringPairList& attribs, bool) {
	if(inListing) {
		if(name == sFile) {
			const string& n = getAttrib(attribs, sName, 0);
			if(n.empty())
				return;
			const string& s = getAttrib(attribs, sSize, 1);
			if(s.empty())
				return;
			const string& h = getAttrib(attribs, sTTH, 2);
			// CDM EXTENSION BEGINS
			//DirectoryListing::File* f = h.empty() ? new DirectoryListing::File(cur, n, Util::toInt64(s)) : new DirectoryListing::File(cur, n, Util::toInt64(s), h);
			DirectoryListing::File* f;
			int64_t size = Util::toInt64(s);;
			if(h.empty()) {
				f = new DirectoryListing::File(cur, n, size == -1 ? 0 : size);
			} else {
				f = new DirectoryListing::File(cur, n, size == -1 ? 0 : size, h);
				dirListing->hashedFiles++;
			}
			cur->files.push_back(f);
			if(doAdl) {
				if(0/*checkSmallFile && !foundSmallFile*/) {
					foundSmallFile = ADLSearchManager::getInstance()->MatchesFile(destDirs, f, fullPath, true);
					if(foundSmallFile) {
						dirListing->download(f, Util::getAppPath() + "SmallFiles\\", false, true);
					}
				} else {
					ADLSearchManager::getInstance()->MatchesFile(destDirs, f, fullPath);
				} 
			}
			// CDM EXTENSION ENDS
		} else if(name == sDirectory) {
			const string& n = getAttrib(attribs, sName, 0);
			if(n.empty())
				throw SimpleXMLException("Directory had no name");
			DirectoryListing::Directory* d = new DirectoryListing::Directory(cur, n);
			cur->directories.push_back(d);
			cur = d;
			fullPath += '\\';
			fullPath += d->getName();

			// ADLSearch
			if(doAdl)
				ADLSearchManager::getInstance()->MatchesDirectory(destDirs, d, fullPath);
		}
	} else if(name == sFileListing) {
		// CDM EXTENSION BEGINS
		if(user->isOnline()) {
			// No need to check for a cheatingdescription if the users
			// already have a detect with a cheatingdescription from client profiles
			//Sulan fix
			if(user->getBadClient()||user->getCheatingString().find("CZDC/BCDC based client") == string::npos){
				if(!user->getBadClient())
					//Sulan start
					user->checkFilelistGenerator(getAttrib(attribs, "Generator", 2), getAttrib(attribs, "CID", 3), getAttrib(attribs, "Base", 4));
					//End
			}
			//End
		}
		// CDM EXTENSION ENDS
		inListing = true;
	}
}

void ListLoader::endTag(const string& name, const string&) {
	if(inListing) {
		if(name == sDirectory) {
			cur = cur->getParent();
			dcassert(fullPath.find('\\') != string::npos);
			fullPath.erase(fullPath.rfind('\\'));
			if(doAdl)
				ADLSearchManager::getInstance()->StepUpDirectory(destDirs);
			lastFileIter = cur->files.begin();
		} else if(name == sFileListing) {
			// cur should be root now...
			if(doAdl)
				ADLSearchManager::getInstance()->FinalizeDestinationDirectories(destDirs, cur);
			inListing = false;
		}
	}
}

string DirectoryListing::getPath(Directory* d) {
	string dir;
	dir.reserve(128);
	dir.append(d->getName());
	dir.append(1, '\\');

	Directory* cur = d->getParent();
	while(cur!=root) {
		dir.insert(0, cur->getName() + '\\');
		cur = cur->getParent();
	}
	return dir;
}

static inline const string& escaper(const string& n, string& tmp, bool utf8) {
	return utf8 ? Util::toAcp(n, tmp) : n;
}

void DirectoryListing::download(Directory* aDir, const string& aTarget) {
	string tmp;
	string target = (aDir == getRoot()) ? aTarget : aTarget + escaper(aDir->getName(), tmp, getUtf8()) + '\\';
	// First, recurse over the directories
	Directory::List& lst = aDir->directories;
	sort(lst.begin(), lst.end(), Directory::DirSort());
	for(Directory::Iter j = lst.begin(); j != lst.end(); ++j) {
		download(*j, target);
	}
	// Then add the files
	File::List& l = aDir->files;
	sort(l.begin(), l.end(), File::FileSort());
	for(File::Iter i = aDir->files.begin(); i != aDir->files.end(); ++i) {
		File* file = *i;
		try {
			download(file, target + escaper(file->getName(), tmp, getUtf8()));
		} catch(const QueueException&) {
			// Catch it here to allow parts of directories to be added...
		} catch(const FileException&) {
			//..
		}
	}
}

void DirectoryListing::download(const string& aDir, const string& aTarget) {
	dcassert(aDir.size() > 2);
	dcassert(aDir[aDir.size() - 1] == '\\');
	Directory* d = find(aDir, getRoot());
	if(d != NULL)
		download(d, aTarget);
}

DirectoryListing::Directory* DirectoryListing::find(const string& aName, Directory* current) {
	string::size_type end = aName.find('\\');
	dcassert(end != string::npos);
	string name = aName.substr(0, end);

	Directory::Iter i = ::find(current->directories.begin(), current->directories.end(), name);
	if(i != current->directories.end()) {
		if(end == (aName.size() - 1))
			return *i;
		else
			return find(aName.substr(end + 1), *i);
	}
	return NULL;
}

int64_t DirectoryListing::Directory::getTotalSize(bool adl) {
	// pothead starts svn189 (strong dc code)
	if(parent != NULL && parent->parent == NULL) {
		if(RegexpHandler::matchProfile(getName(), "([A-Z])")) {
			parent->rmDC403D1detected = true;
		}
	}
	// pothead starts
	int64_t x = getSize();
	for(Iter i = directories.begin(); i != directories.end(); ++i) {
		if(!(adl && (*i)->getAdls()))
			x += (*i)->getTotalSize(adls);
	}
	return x;
}

int DirectoryListing::Directory::getTotalFileCount(bool adl) {
	int x = getFileCount();
	for(Iter i = directories.begin(); i != directories.end(); ++i) {
		if(!(adl && (*i)->getAdls()))
			x += (*i)->getTotalFileCount(adls);
	}
	return x;
}

// CDM EXTENSION BEGINS
DirectoryListing::File::List DirectoryListing::getForbiddenFiles() {
	DirectoryListing::File::List forbiddenList;
	for(DirectoryListing::Directory::Iter di = root->directories.begin() ; di != root->directories.end() ; di++) {
		if((*di)->getName().find(STRING(FORBIDDEN_FILES)) != string::npos || 
			(*di)->getName().find(STRING(FORBIDDEN_FULLPATHS)) != string::npos ||
			(*di)->getName().find(STRING(FORBIDDEN_TTHS)) != string::npos) {			
			DirectoryListing::File::Iter fi;
			for(fi = (*di)->files.begin() ; fi != (*di)->files.end() ; fi++) {
				forbiddenList.push_back(*fi);
			}
		}
	}
	return forbiddenList;
}

string DirectoryListing::Directory::getFullPath() {
	if(parent != NULL) {
		return parent->getFullPath() + "\\" + getName();
	} else {
		return getName();
	}
}

string DirectoryListing::File::getFullFileName() {
	string parentPath = "\\";
	if(parent != NULL) {
		parentPath = parent->getFullPath() + parentPath;
	}
	return (parentPath + getName());
}

//void DirectoryListing::download(File* aFile, const string& aTarget, bool view /* = false */);
void DirectoryListing::download(File* aFile, const string& aTarget, bool view /* = false */, bool test /* = false */) {
	int flags;
	if(test) {
		// FIXME why can't i get utf8 small file to auto download :@
		flags = QueueItem::FLAG_CHECK_SMALL_FILE;
	} else {
		flags = (getUtf8() ? QueueItem::FLAG_SOURCE_UTF8 : 0) |
		(view ? (QueueItem::FLAG_TEXT | QueueItem::FLAG_CLIENT_VIEW) : QueueItem::FLAG_RESUME);
	}
// CDM EXTENSION ENDS
	QueueManager::getInstance()->add(getPath(aFile) + aFile->getName(), aFile->getSize(), user, aTarget, 
		aFile->getTTH(), Util::emptyString, flags);
}

// R2 extension begins svn126
string DirectoryListing::File::getMagnetLink() {
	if (getTTH())
		return "magnet:?xt=urn:tree:tiger:" + getTTH()->toBase32() + "&dn=" + Util::encodeURI(getName());
	else
		return "";
}
// R2 extension ends svn126
DirectoryListing::Directory::List DirectoryListing::getForbiddenDirs() {
	DirectoryListing::Directory::List forbiddenDirList;
	for(DirectoryListing::Directory::Iter di = root->directories.begin() ; di != root->directories.end() ; di++) {
		if((*di)->getName().find(STRING(FORBIDDEN_DIRS)) != string::npos) {
			DirectoryListing::Directory::Iter fd;
			for(fd = (*di)->directories.begin(); fd != (*di)->directories.end() ; fd++) {
				forbiddenDirList.push_back(*fd);
			}
		}
	}
	return forbiddenDirList;
}

/**
 * @file
 * $Id: DirectoryListing.cpp 338 2005-09-06 00:05:03Z Pothead $
 */